/**
 * @file      adc.c
 * @author    chipsea
 * @brief     Contains all functions support for adc driver
 * @version   0.1
 * @date      2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */
#include <string.h>
#include "error.h"
#include "gpio.h"
#include "pwrmgr.h"
#include "clock.h"
#include "adc.h"
#include "log.h"
#include "jump_function.h"
#include "halPeripheral.h"

 extern volatile sysclk_t g_system_clk;

typedef struct _adc_Contex_t
{
    bool        enable;
    uint8_t     all_channel;
    uint8_t     chs_en_shadow;
    bool        continue_mode;     //manual/auto

    //sysclk_t    clk_src;
    uint16_t    adc_cal_postive;
    uint16_t    adc_cal_negtive;

    adc_Hdl_t   evt_handler;
} adc_Ctx_t;

static adc_Ctx_t mAdc_Ctx = 
{
    .enable = FALSE,
    .all_channel = 0x00,
    .chs_en_shadow = 0x00,
    .continue_mode = FALSE,
    .adc_cal_postive = 0xFFF,
    .adc_cal_negtive = 0xFFF,
    .evt_handler = NULL
};

#define ADC_SAMPING_NUM       32
#define ADC_MAX_OFFSET        64

#define FILTER_SIZE   4
uint16_t filter(uint32_t base,int ch,int n)
{
    char count,j;
    int offset;
    uint16_t    max ,min ;

    uint16_t value_buf[FILTER_SIZE]; 

    int  sum=0; 
    offset = n;
    for  (count=0;count<FILTER_SIZE;count++) 
    {
        value_buf[count ++]= (uint16_t)(read_reg(ADC_CH_BASE + (ch * 0x80) + ((base + offset)<<2))&0xfff);
        value_buf[count] = (uint16_t)((read_reg(ADC_CH_BASE + (ch * 0x80) + ((base + offset)<<2))>>16)&0xfff);
        offset ++;
    }
    max = value_buf[0];
    min = value_buf[0];
    sum = value_buf[0];
    for(j=1;j<FILTER_SIZE;j++)
    {
        max = (max > value_buf[j])?  max : value_buf[j];
        min = (min > value_buf[j])? value_buf[j] : min ;
        sum += value_buf[j]; 
    }

    sum -= (max + min);
    return (uint16_t)(sum>>1);
}
/**
 * @fn      HalAdcLoadCalibrationValue
 * @brief   Read calibration values from Flash
 * @param   none
 * @return  none
 */
static void HalAdcLoadCalibrationValue(void)
{
    uint32_t adc_cal = read_reg(SPIF_RSVD1_ADC_CALIBRATE);
    mAdc_Ctx.adc_cal_negtive = (uint16_t)(adc_cal&0x0fff);
    mAdc_Ctx.adc_cal_postive = (uint16_t)((adc_cal>>16)&0x0fff);
    LOG("AD_CAL[%x %x]\n",mAdc_Ctx.adc_cal_negtive,mAdc_Ctx.adc_cal_postive);

  if((mAdc_Ctx.adc_cal_negtive < 0x733) || (mAdc_Ctx.adc_cal_negtive > 0x8cc) ||
            (mAdc_Ctx.adc_cal_postive < 0x733) || (mAdc_Ctx.adc_cal_postive > 0x8cc))
    {
        mAdc_Ctx.adc_cal_negtive = 0xfff;
        mAdc_Ctx.adc_cal_postive = 0xfff;
        LOG("->AD_CAL[%x %x]\n",mAdc_Ctx.adc_cal_negtive, mAdc_Ctx.adc_cal_postive);
    }
}

GpioPin_t s_pinmap[ADC_CH_NUM] = {
    GPIO_DUMMY, //ADC_CH0 =0,
    GPIO_DUMMY, //ADC_CH1 =1,
    P11, //ADC_CH1N =2,
    P23, //ADC_CH1P =3,  
    P24, //ADC_CH2N =4,
    P14, //ADC_CH2P =5,  
    P15, //ADC_CH3N =6,
    P20, //ADC_CH3P =7, 
    GPIO_DUMMY,  //ADC_CH_VOICE =8,
};

/**
 * @fn      SetSamplingResolution
 * @brief   Set the sampling resolution
 * @param   channel
 * @param   is_high_resolution
 * @param   is_differential_mode
 * @return  none
 */
static void SetSamplingResolution(AdcChannel_t channel, bool is_high_resolution)
{
    uint8_t aio = 0;
    switch(channel)
    {                
        case ADC_CH1N_P11:
            aio = 0;    
            break;
        case ADC_CH1P_P23:
            aio = 1;
            break;
        case ADC_CH2N_P24:
            aio = 2;
            break;
        case ADC_CH2P_P14:
            aio = 3;
            break;
        case ADC_CH3N_P15:
            aio = 4;
            break;
        case ADC_CH3P_P20:
            aio = 7;
            break;
        default:
            return;
    }
    if(is_high_resolution)
    {
        subWriteReg(&(AP_AON->PMCTL2_1),(aio+8),(aio+8),0);
        subWriteReg(&(AP_AON->PMCTL2_1),aio,aio,1);            
    }
    else
    {
        subWriteReg(&(AP_AON->PMCTL2_1),(aio+8),(aio+8),1);
        subWriteReg(&(AP_AON->PMCTL2_1),aio,aio,0);
    }
}

/**
 * @fn      SetSamplingResolutionAuto
 * @brief   Set the automatic sampling resolution
 * @param   channel
 * @param   is_high_resolution
 * @param   is_differential_mode
 * @return  none
 */
static void SetSamplingResolutionAuto(uint8_t channel, uint8_t is_high_resolution)
{
    uint8_t i_channel;
    AdcChannel_t a_channel;

    AP_AON->PMCTL2_1 = 0x00;
    for(i_channel =MIN_ADC_CH;i_channel<=MAX_ADC_CH;i_channel++)
    {
        if(channel & BIT(i_channel))
        {
            a_channel = (AdcChannel_t)i_channel;
            SetSamplingResolution(a_channel,(is_high_resolution & BIT(i_channel)));
        }
    }
}

/**
 * @fn      SetDifferentialMode
 * @brief   Set the difference mode
 * @param   none
 * @return  none
 */
static void SetDifferentialMode(void)
{
    subWriteReg(&( AP_PCRM->ANA_CTL),8,8,0);
    subWriteReg(&( AP_PCRM->ANA_CTL),11,11,0);
}

/**
 * @fn      DisableAnalogPin
 * @brief   disable analog pin
 * @param   channel
 * @return  none
 */
static void DisableAnalogPin(AdcChannel_t channel)
{
    int index = (int)channel;
    GpioPin_t pin = s_pinmap[index];
    if(pin == GPIO_DUMMY)
        return;

    HalGpioAnalogConfig(pin,Bit_DISABLE);
    HalGpioPinInit(pin,GPIO_INPUT);          //ie=0,oen=1 set to imput
    HalGpioPupdConfig(pin,GPIO_FLOATING);    //
}

/**
 * @fn      ClearAdcConfig
 * @brief   Clear configuration
 * @param   none
 * @return  none
 */
static void ClearAdcConfig(void)
{
    mAdc_Ctx.all_channel = 0x00;
    mAdc_Ctx.chs_en_shadow = 0x00;
    mAdc_Ctx.continue_mode = FALSE;
    mAdc_Ctx.evt_handler = NULL;
}

/**
 * @fn      HalAdcIRQHandler
 * @brief   This function process for adc interrupt
 * @param   none
 * @return  none
 */
volatile int flag = 0;
void __attribute__((used)) HalAdcIRQHandler(void)
{
    int ch,status,ch2,n;
    uint16_t adc_data[MAX_ADC_SAMPLE_SIZE];
    status = GET_IRQ_STATUS;
    MASK_ADC_INT;

    if(status == mAdc_Ctx.all_channel)
    {
        for(ch = MIN_ADC_CH; ch <= MAX_ADC_CH; ch++)
        {
            if(status & BIT(ch))
            {
								if(mAdc_Ctx.continue_mode == FALSE)
			          {
				            mAdc_Ctx.all_channel &= ~BIT(ch);//disable channel				
			          }
			
                AP_ADCC->intr_mask &= ~BIT(ch);

                for(n = 0; n < (MAX_ADC_SAMPLE_SIZE-3); n++)
                {
                    adc_data[n] = (uint16_t)(read_reg(ADC_CH_BASE + (ch * 0x80) + ((n+2) * 4))&0xfff);
                    adc_data[n+1] = (uint16_t)((read_reg(ADC_CH_BASE + (ch * 0x80) + ((n+2) * 4))>>16)&0xfff);
                }

                AP_ADCC->intr_clear = BIT(ch);

                if(mAdc_Ctx.enable == FALSE)
                    continue;

                ch2=(ch%2)?(ch-1):(ch+1);

                if (mAdc_Ctx.evt_handler)
                {
                    AdcEvt_t evt;
                    evt.type = HAL_ADC_EVT_DATA;
                    evt.ch = (AdcChannel_t)ch2;
                    evt.data = adc_data;
                    evt.size = MAX_ADC_SAMPLE_SIZE-3;
                    mAdc_Ctx.evt_handler(&evt);
                }

                AP_ADCC->intr_mask |= BIT(ch);
            }
        }

        if((mAdc_Ctx.all_channel == 0) && (mAdc_Ctx.continue_mode == FALSE))//
        {
            HalAdcStop();
        }
    }

    ENABLE_ADC_INT;
}

/**
 * @fn      AdcWakeupHdl
 * @brief   Set the ADC interrupt priority
 * @param   none
 * @return  none
 */
static void AdcWakeupHdl(void)
{
    NVIC_SetPriority((IRQn_Type)ADCC_IRQn, IRQ_PRIO_HAL);
}

/**
 * @fn      HalAdcInit
 * @brief   This function process for adc initial
 * @param   none
 * @return  none
 */
void HalAdcInit(void)
{
    hal_pwrmgr_register(MOD_ADCC,NULL,AdcWakeupHdl);
    ClearAdcConfig();
    HalAdcLoadCalibrationValue();
    mAdc_Ctx.enable = TRUE;
}

/**
 * @fn      HalAdcClockConfig
 * @brief   This function process for adc sample frequency
 * @param   clk      adc sampling rate
 * @return  ErrCode_t
 */
int HalAdcClockConfig(AdcClockSel_t clk)
{
    if(mAdc_Ctx.enable == FALSE)
    {
        return ERR_NOT_REGISTED;
    }
    if(clk > HAL_ADC_CLOCK_320K)
    {
        return ERR_INVALID_PARAM;
    }
    subWriteReg(0x4000F000 + 0x7c,2,1,clk);
    return SUCCESS;
}

/**
 * @fn      HalAdcStart
 * @brief   start adc
 * @param   none
 * @return  ErrCode_t
 */
int HalAdcStart(void)
{
    uint8_t     all_channel2 = (((mAdc_Ctx.chs_en_shadow&0x80)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x40)<<1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x20)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x10)<<1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x08)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x04)<<1));
    if(mAdc_Ctx.enable == FALSE)
    {
        return ERR_NOT_REGISTED;
    }

    hal_pwrmgr_lock(MOD_ADCC);

    JUMP_FUNCTION(ADCC_IRQ_HANDLER)                  =   (uint32_t)&HalAdcIRQHandler;
    //ENABLE_ADC;
    for(int i=MIN_ADC_CH; i<=MAX_ADC_CH; i++)
    {
        if(all_channel2 & (BIT(i)))
        {
            switch (i)
            {
            case ADC_CH1N_P11:
                AP_PCRM->ADC_CTL1 |= BIT(20);
                break;

            case ADC_CH1P_P23:
                AP_PCRM->ADC_CTL1 |= BIT(4);
                break;

            case ADC_CH2N_P24:
                AP_PCRM->ADC_CTL2 |= BIT(20);
                break;

            case ADC_CH2P_P14:
                AP_PCRM->ADC_CTL2 |= BIT(4);
                break;

            case ADC_CH3N_P15:
                AP_PCRM->ADC_CTL3 |= BIT(20);
                break;

            case ADC_CH3P_P20:
                AP_PCRM->ADC_CTL3 |= BIT(4);
                break;
            }
        }
    }
    AP_PCRM->ANA_CTL |= BIT(3);
    AP_PCRM->ANA_CTL |= BIT(0);//new

    //ADC_IRQ_ENABLE;
    NVIC_EnableIRQ((IRQn_Type)ADCC_IRQn);
    //ENABLE_ADC_INT;
    AP_ADCC->intr_mask = mAdc_Ctx.all_channel;

    //disableSleep();
    return SUCCESS;
}

/**
 * @fn      HalAdcChannelConfig
 * @brief   This function process for adc config
 * @param   cfg          adc initial struct
            handler      adc interrupt callback
 * @return  ErrCode_t
 */
int HalAdcChannelConfig(AdcCfg_t cfg, adc_Hdl_t evt_handler)
{
    uint8_t i;
    if(mAdc_Ctx.enable == FALSE)
    {
        return ERR_NOT_REGISTED;
    }

    if(evt_handler == NULL)
    {
        return ERR_INVALID_PARAM;
    }

    if((cfg.channel_flag & BIT(0)) || (cfg.channel_flag & BIT(1)))
    {
        return ERR_NOT_SUPPORTED;
    }

    ClearAdcConfig();

    AP_AON->PMCTL2_1 = 0x00;
    AP_PCRM->ANA_CTL &= ~BIT(0);
    AP_PCRM->ANA_CTL &= ~BIT(3);
    hal_clk_gate_disable(MOD_ADCC);
    hal_clk_reset(MOD_ADCC);

    mAdc_Ctx.continue_mode = cfg.is_continue_mode;
    mAdc_Ctx.all_channel = cfg.channel_flag & 0x03;
    for(i=2;i<8;i++)
    {
        if(cfg.channel_flag & BIT(i))
        {
            if(i%2)
            {
                mAdc_Ctx.all_channel |= BIT(i-1);
            }
            else
            {
                mAdc_Ctx.all_channel |= BIT(i+1);
            }
        }
    }
    mAdc_Ctx.chs_en_shadow = mAdc_Ctx.all_channel;
    if((AP_PCR->SW_CLK & BIT(MOD_ADCC)) == 0)
    {
        hal_clk_gate_enable(MOD_ADCC);
    }

    //CLK_1P28M_ENABLE;
    AP_PCRM->CLKSEL |= BIT(6);

    //ENABLE_XTAL_OUTPUT;         //enable xtal 16M output,generate the 32M dll clock
    AP_PCRM->CLKHF_CTL0 |= BIT(18);

    //ENABLE_DLL;                  //enable DLL
    AP_PCRM->CLKHF_CTL1 |= BIT(7);

    //ADC_DBLE_CLOCK_DISABLE;      //disable double 32M clock,we are now use 32M clock,should enable bit<13>, diable bit<21>
    AP_PCRM->CLKHF_CTL1 &= ~BIT(21);//check
     //subWriteReg(0x4000F044,21,20,3);

    //ADC_CLOCK_ENABLE;            //adc clock enbale,always use clk_32M
    AP_PCRM->CLKHF_CTL1 |= BIT(13);

    //subWriteReg(0x4000f07c,4,4,1);    //set adc mode,1:mannual,0:auto mode
    AP_PCRM->ADC_CTL4 |= BIT(4);
//      AP_PCRM->ADC_CTL4 |= BIT(3);
    AP_PCRM->ADC_CTL4 |= BIT(0);
//    AP_PCRM->ADC_CTL4 &= ~BIT(3);
    SetSamplingResolutionAuto(cfg.channel_flag, cfg.is_high_resolution);

    AP_PCRM->ADC_CTL0 &= ~BIT(20);
    AP_PCRM->ADC_CTL0 &= ~BIT(4);
    AP_PCRM->ADC_CTL1 &= ~BIT(20);
    AP_PCRM->ADC_CTL1 &= ~BIT(4);
    AP_PCRM->ADC_CTL2 &= ~BIT(20);
    AP_PCRM->ADC_CTL2 &= ~BIT(4);
    AP_PCRM->ADC_CTL3 &= ~BIT(20);
    AP_PCRM->ADC_CTL3 &= ~BIT(4);

    AP_PCRM->ANA_CTL &= ~BIT(23); //disable micbias
    AP_PCRM->ADC_CTL4 &= ~BIT(4); //enable auto mode
    mAdc_Ctx.evt_handler = evt_handler;
    for(i=MIN_ADC_CH;i<=MAX_ADC_CH;i++)
    {
        if(cfg.channel_flag & BIT(i))
        {
            GpioPin_t pin = s_pinmap[i];
            HalGpioPupdConfig(pin,GPIO_FLOATING);
            HalGpioDsControl(pin, Bit_ENABLE);
            HalGpioAnalogConfig(pin, Bit_ENABLE);

            switch (i)
            {
                case 0:
                        AP_PCRM->ADC_CTL0 |= BIT(20);
                    break;
                case 1:
                        AP_PCRM->ADC_CTL0 |= BIT(4);
//                 AP_ADCC->compare_cfg[0] = (ADC_SAMPING_NUM<<24);
                    break;
                case 2:
                        AP_PCRM->ADC_CTL1 |= BIT(20);
//                  AP_ADCC->compare_cfg[1] = (ADC_SAMPING_NUM<<24);
                    break;
                case 3:
                        AP_PCRM->ADC_CTL1 |= BIT(4);
//                    AP_ADCC->compare_cfg[2] = (ADC_SAMPING_NUM<<24);
                    break; 
                case 4:
                        AP_PCRM->ADC_CTL2 |= BIT(20);
//                    AP_ADCC->compare_cfg[3] = (ADC_SAMPING_NUM<<24);
                    break;
                case 5:
                        AP_PCRM->ADC_CTL2 |= BIT(4);
//                    AP_ADCC->compare_cfg[4] = (ADC_SAMPING_NUM<<24);
                    break;
                case 6:
                        AP_PCRM->ADC_CTL3 |= BIT(20);
//                    AP_ADCC->compare_cfg[5] = (ADC_SAMPING_NUM<<24);
                    break;
                case 7:
                        AP_PCRM->ADC_CTL3 |= BIT(4);
//                    AP_ADCC->compare_cfg[6] = (ADC_SAMPING_NUM<<24);
                    break;
                default:
                    break;
            }
        }
    }
    return SUCCESS;
}

/**
 * @fn      HalAdcStop
 * @brief   stop adc
 * @param   none
 * @return  ErrCode_t
 */
int HalAdcStop(void)
{
    int i;
    uint8_t     all_channel2 = (((mAdc_Ctx.chs_en_shadow&0x80)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x40)<<1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x20)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x10)<<1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x08)>>1)|\
                                ((mAdc_Ctx.chs_en_shadow&0x04)<<1));
    if(mAdc_Ctx.enable == FALSE)
    {
        return ERR_NOT_REGISTED;
    }
    //MASK_ADC_INT;
    AP_ADCC->intr_mask = 0x1ff;

    NVIC_DisableIRQ((IRQn_Type)ADCC_IRQn);
    JUMP_FUNCTION(ADCC_IRQ_HANDLER)                  =   0;

    AP_ADCC->intr_clear = 0x1FF;

    //DISABLE_ADC;
    AP_PCRM->ANA_CTL &= ~BIT(3);

    //ADC_CLOCK_DISABLE;
    if(g_system_clk != SYS_CLK_DBL_32M)
    {
        AP_PCRM->CLKHF_CTL1 &= ~BIT(13);
    }

    for(i =MIN_ADC_CH; i<= MAX_ADC_CH; i++)
    {
        if(all_channel2 & BIT(i))
        {
            DisableAnalogPin((AdcChannel_t)i);
        }
    }
     AP_PCRM->ANA_CTL &= ~BIT(0);//Power down analog LDO
    hal_clk_reset(MOD_ADCC);
    hal_clk_gate_disable(MOD_ADCC);

    ClearAdcConfig();
    //enableSleep();

    hal_pwrmgr_unlock(MOD_ADCC);
    return SUCCESS;
}

const unsigned int adc_Lambda[MAX_ADC_CH - MIN_ADC_CH + 1] =
{
    4519,//P11
    4308,//P23
    4263,//P24
    4482,//P14
    4180,//P15
    4072,//P20
};

/**
 * @fn      HalAdcValueCal
 * @brief   This function calculate the real time vol value
 * @param   ch           adc channel
            *buf         adc sample data
            size         data size
            high_resol   work mode
 * @return  vol value
 */
int32_t HalAdcValueCal(AdcChannel_t ch,uint16_t* buf, uint32_t size, uint8_t high_resol)
{
    uint32_t i;
    int adc_sum = 0;
    volatile int32_t result = 0;
    uint16_t adc_cal_postive = mAdc_Ctx.adc_cal_postive;
    uint16_t adc_cal_negtive = mAdc_Ctx.adc_cal_negtive;
    if(ch< MIN_ADC_CH || ch > MAX_ADC_CH)
    {
        return ERR_INVALID_PARAM;
    }

    for (i = 0; i < size; i++)
    {
       adc_sum += (buf[i]&0xfff);
    }

    HalAdcLoadCalibrationValue();
    result = adc_sum/size;
    if((adc_cal_postive!=0xfff)&&(adc_cal_negtive!=0xfff))
    {
        int delta = ((int)(adc_cal_postive-adc_cal_negtive))>>1;
        if(ch&0x01)
        {
            result =  ((result-delta)*1000 /(adc_cal_postive+adc_cal_negtive));
        }
        else
        {
            result =  ((result+delta)*1000 /(adc_cal_postive+adc_cal_negtive));
        }
        
        if(result < 0)
        {
            result = 0;
        }
    }
    else
    {
        result =  ((result*1000) >>12);
    }

    if(high_resol == TRUE)
    {
        result = result*8/10;
    }
    else
    {
        result = result*adc_Lambda[ch-2]*8/10000;
    }

    return result;
}
